package follow

import (
	"errors"
	"gitee.com/mootok/mootok/src/constant/strings"
	"gitee.com/mootok/mootok/src/constant/strings/redis"
	"gitee.com/mootok/mootok/src/models"
	"gitee.com/mootok/mootok/src/services/feed"
	"gitee.com/mootok/mootok/src/services/user"
	"gitee.com/mootok/mootok/src/storage/database"
	redis2 "gitee.com/mootok/mootok/src/storage/redis"
	"gitee.com/mootok/mootok/src/utils/logging"
	"gitee.com/mootok/mootok/src/web/api"
	"gitee.com/mootok/mootok/src/web/common/response"
	"github.com/gin-gonic/gin"
	redis3 "github.com/redis/go-redis/v9"
	"github.com/sirupsen/logrus"
	"net/http"
	"strconv"
)

type ApiFollowImpl struct {
}

var UserService user.Service

func (*ApiFollowImpl) FollowUserById(c *gin.Context, id int) {
	//401
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Follow.User.Post").WithFields(logrus.Fields{
		"actor_id": userId,
		"id":      id,
	})

	// 判断用户是否存在
	exists, err := UserService.IsExists(id)

	if err != nil {
		logger.Errorf("user select from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	//用户不存在
	if !exists {
		response.FailWithMsg(c, http.StatusNotFound, strings.UserNotExistedCode, strings.UserNotExisted)
		return
	}
	//本人
	if id == userId {
		response.SuccessWithMsg(c, strings.UnableToRelateYourselfErrorCode, strings.UnableToRelateYourselfError)
		return
	}

	// 开始事务
	tx := database.Client.WithContext(c).Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
	}()

	var count int64
	if err = tx.Model(&models.FollowRelation{}).Where("fans_id = ?", userId).Where("follow_id = ?", id).
		Count(&count).Error; err != nil {
		logger.Errorf("follower_relation select from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	if count > 0 {
		response.SuccessWithMsg(c, strings.AlreadyFollowingErrorCode, strings.AlreadyFollowingError)
		return
	}

	//create
	followRelation := models.FollowRelation{
		FansId:   userId,
		FollowId: id,
	}

	if err := tx.Model(&models.FollowRelation{}).Create(&followRelation).Error; err != nil {
		logger.Errorf("follow_relations creation from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
		return
	}

	//update follow cache
	err = updateRelationCacheAfterFollow(c, followRelation, logger)
	if err != nil {
		logger.Errorf("updateFolloweeIdListCache to redis failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		return
	}
	//因为没有使用MQ所以先修改数据库再删缓存保证数据一致性
	err = DelCountCacheAfterFollow(c, followRelation, logger)
	if err != nil {
		logger.Errorf("removeFollowIdAndFansIdListCache to redis failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisDelValueErrorCode, strings.InternalServiceError)
		return
	}

	if err = feed.FollowFeedOperation(c, followRelation, logger); err != nil {
		logger.Errorf("OperateUserVideoFeedRedisAfterFollowOrUnFollow err %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		return
	}
	response.Success(c, strings.SuccessCode)
	logger.Errorf("followUserById success")
}

func (*ApiFollowImpl) UnFollowUserById(c *gin.Context, id int) {
	//401
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Follow.User.Delete").WithFields(logrus.Fields{
		"actor_id": userId,
		"id":      id,
	})

	// 判断用户是否存在
	exists, err := UserService.IsExists(id)

	if err != nil {
		logger.Errorf("user select from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	//用户不存在
	if !exists {
		response.FailWithMsg(c, http.StatusNotFound, strings.UserNotExistedCode, strings.UserNotExisted)
		return
	}

	//本人
	if id == userId {
		response.SuccessWithMsg(c, strings.UnableToRelateYourselfErrorCode, strings.UnableToRelateYourselfError)
		return
	}

	//delete
	followRelation := models.FollowRelation{
		FansId:   userId,
		FollowId: id,
	}

	// 开始事务
	tx := database.Client.WithContext(c).Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
	}()

	if err = tx.Where("follow_id = ?", followRelation.FollowId).
		Where("fans_id = ?", followRelation.FansId).Delete(&models.FollowRelation{}).Error; err != nil {
		logger.Errorf("follow_relations creation from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
		return
	}

	if err := updateRelationCacheAfterUnFollow(c, followRelation, logger); err != nil {
		logger.Errorf("updateFolloweeIdListCache to redis failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		return
	}

	if err := DelCountCacheAfterUnFollow(c, followRelation, logger); err != nil {
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisDelValueErrorCode, strings.InternalServiceError)
		return
	}

	if err := feed.UnFollowFeedOperation(c, followRelation, logger); err != nil {
		logger.Errorf("OperateUserVideoFeedRedisAfterFollowOrUnFollow err %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		return
	}
	response.Success(c, strings.SuccessCode)
	logger.Errorf("unFollowUserById success")
}

func (*ApiFollowImpl) PageFollowById(c *gin.Context, fansId int, params api.PageFollowByIdParams) {
	//login status
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)
	redisKey := genFollowIdScoreListKey(fansId)

	var followIdScoreList []redis3.Z

	followIdScoreList, err := redis2.Client.ZRevRangeByScoreWithScores(c, redisKey, &redis3.ZRangeBy{
		Max:    params.CursorId,
		Offset: params.Offset,
		Min:    "0",
		Count:  int64(10),
	}).Result()

	logger := logging.LogService("Follow.Get").WithFields(logrus.Fields{
		"fans_id": fansId,
		"params":  params,
		"user_id": userId,
	})

	if err != nil {
		logger.Errorf("redis get key error %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		return
	}
	// 判断用户是否存在
	exists, err := UserService.IsExists(fansId)

	if err != nil {
		logger.Errorf("user select from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	//用户不存在
	if !exists {
		response.FailWithMsg(c, http.StatusNotFound, strings.UserNotExistedCode, strings.UserNotExisted)
		return
	}
	//构建数据
	if len(followIdScoreList) == 0 {

		//查询关注
		var followRelationList []models.FollowRelation
		result := database.Client.Model(&models.FollowRelation{}).Where("fans_id = ?", fansId).
			Order("created_at desc").Find(&followRelationList)

		if err := result.Error; err != nil {
			logger.Errorf("follower_relation select from db failed %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}

		//把数据添加到redis中
		for i := 0; i < len(followRelationList); i++ {
			score := float64(followRelationList[i].CreatedAt.Unix())
			err := redis2.Client.ZAdd(c, redisKey, redis3.Z{
				Score:  score,
				Member: followRelationList[i].FollowId,
			}).Err()
			//todo 事务
			if err != nil {
				logger.Errorf("follow id set to redis failed %v", err)
				response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisSetValueErrorCode, strings.InternalServiceError)
				return
			}
		}

		//再次查询
		followIdScoreList, err = redis2.Client.ZRevRangeByScoreWithScores(c, redisKey, &redis3.ZRangeBy{
			Max:    params.CursorId,
			Offset: params.Offset,
			Min:    "0",
			Count:  int64(10),
		}).Result()

	}

	//从redis里面拿数据
	var minScore float64
	var offset int64
	offset = 1

	followIdList := make([]int, len(followIdScoreList))
	for i := 0; i < len(followIdScoreList); i++ {
		score := followIdScoreList[i].Score
		id, _ := strconv.Atoi(followIdScoreList[i].Member.(string))
		followIdList[i] = id
		if score == minScore {
			offset++
		} else {
			//reset
			minScore = score
			offset = 1
		}
	}

	cursorId := strconv.FormatFloat(minScore, 'f', -1, 64)
	//get userInfo
	follows := make([]api.UserInfo, len(followIdList))

	if err := UserService.GetUserInfo(followIdList, &userId, &follows); err != nil {
		logger.Errorf("failed convert to UserInfo: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, &api.UserCursoredListResp{
		Items:    &follows,
		Offset:   &offset,
		CursorId: &cursorId,
	})

	logger.Errorf("success")

	logger.Errorf("pageFollow success")
}

func (*ApiFollowImpl) PageFansById(c *gin.Context, followId int, params api.PageFansByIdParams) {
	redisKey := genFansIdScoreListKey(followId)
	//login status
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	var fansIdScoreList []redis3.Z
	fansIdScoreList, err := redis2.Client.ZRevRangeByScoreWithScores(c, redisKey, &redis3.ZRangeBy{
		Max:    params.CursorId,
		Offset: params.Offset,
		Min:    "0",
		Count:  int64(10),
	}).Result()

	logger := logging.LogService("Follow.Fans.Get").WithFields(logrus.Fields{
		"follow_id": followId,
		"params":    params,
	})

	if err != nil {
		logger.Errorf("redis get key error %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		return
	}

	exists, err := UserService.IsExists(followId)
	//404
	if err != nil {
		logger.Errorf("user select from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	//用户不存在
	if !exists {
		response.SuccessWithMsg(c, strings.UserNotExistedCode, strings.UserNotExisted)
		return
	}

	//构建数据
	if len(fansIdScoreList) == 0 {
		var followRelationList []models.FollowRelation
		result := database.Client.Model(&models.FollowRelation{}).Where("follow_id = ?", followId).
			Order("created_at desc").Find(&followRelationList)
		if err := result.Error; err != nil {
			logger.Errorf("follower_relation select from db failed %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}

		//把数据添加到redis中
		for i := 0; i < len(followRelationList); i++ {
			score := float64(followRelationList[i].CreatedAt.Unix())
			err := redis2.Client.ZAdd(c, redisKey, redis3.Z{
				Score:  score,
				Member: followRelationList[i].FansId,
			}).Err()
			if err != nil {
				logger.Errorf("fans id set to redis failed %v", err)
				response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisSetValueErrorCode, strings.InternalServiceError)
				return
			}
		}
		//如果没有则构建数据
		fansIdScoreList, err = redis2.Client.ZRevRangeByScoreWithScores(c, redisKey, &redis3.ZRangeBy{
			Max:    params.CursorId,
			Offset: params.Offset,
			Min:    "0",
			Count:  int64(10),
		}).Result()
	}

	//从redis里面拿数据
	var minScore float64
	var offset int64
	offset = 1
	fansIdList := make([]int, len(fansIdScoreList))
	for i := 0; i < len(fansIdScoreList); i++ {
		score := fansIdScoreList[i].Score
		id, _ := strconv.Atoi(fansIdScoreList[i].Member.(string))
		fansIdList[i] = id
		if score == minScore {
			offset++
		} else {
			//reset
			minScore = score
			offset = 1
		}
	}
	cursorId := strconv.FormatFloat(minScore, 'f', -1, 64)

	//get userInfo
	fans := make([]api.UserInfo, len(fansIdList))

	if err := UserService.GetUserInfo(fansIdList, &userId, &fans); err != nil {
		logger.Errorf("failed convert to UserInfo: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, &api.UserCursoredListResp{
		Items:    &fans,
		Offset:   &offset,
		CursorId: &cursorId,
	})

	logger.Errorf("success")
}

func (*ApiFollowImpl) GetFollowStatusById(c *gin.Context, id int) {
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Follow.Fans.Get").WithFields(logrus.Fields{
		"id":      id,
		"user_id": userId,
	})

	result := database.Client.Where("fans_id = ?", userId).Where("follow_id = ?", id).
		Find(&models.FollowRelation{})
	if err := result.Error; err != nil {
		logger.Errorf("followRelation select  from db falied %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	var followStatus int
	if result.RowsAffected != 0 {
		followStatus = 1
	} else {
		followStatus = 0
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, api.GetFollowStatusResp{
		FollowStatus: &followStatus,
	})
	logger.Errorf("getFollowStatusById success")
}

func (*ApiFollowImpl) GetFollowCountById(c *gin.Context, id int) {
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Follow.Count.Get").WithFields(logrus.Fields{
		"id":      id,
		"user_id": userId,
	})
	// 判断用户是否存在
	exists, err := UserService.IsExists(id)

	if err != nil {
		logger.Errorf("user select from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	//用户不存在
	if !exists {
		response.FailWithMsg(c, http.StatusNotFound, strings.UserNotExistedCode, strings.UserNotExisted)
		return
	}

	//从redis里面查询数据
	redisKey := genFollowUserCountKey(id)
	countCache, err := redis2.Client.Get(c, redisKey).Result()

	var count int64
	if err != nil {
		if errors.Is(err, redis3.Nil) {
			//build
			if err := database.Client.Model(models.FollowRelation{}).Where("fans_id = ?", id).Count(&count).Error; err != nil {
				logger.Errorf("count follow from db failed %v", err)
				response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
				return
			}
			if err = redis2.Client.Set(c, redisKey, count, redis.FansOrFollowCountExpiredTime).Err(); err != nil {
				logger.Errorf("set count to redis failed %v", err)
				response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisSetValueErrorCode, strings.InternalServiceError)
				return
			}
		} else {
			logger.Errorf("redis operation error %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
			return
		}
	} else {
		count, err = strconv.ParseInt(countCache, 10, 64)
		if err != nil {
			logger.Errorf("parse str to int64 error %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.VariableTypeConversionErrorCode, strings.InternalServiceError)
			return
		}
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, api.GetFollowOrFansCountByIdResp{
		Count: &count,
	})

	logger.Errorf("getFollowCountById success")
}

func (*ApiFollowImpl) GetFansCountById(c *gin.Context, id int) {

	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Follow.Count.Get").WithFields(logrus.Fields{
		"id":      id,
		"user_id": userId,
	})
	// 判断用户是否存在
	exists, err := UserService.IsExists(id)

	if err != nil {
		logger.Errorf("user select from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	//用户不存在
	if !exists {
		response.FailWithMsg(c, http.StatusNotFound, strings.UserNotExistedCode, strings.UserNotExisted)
		return
	}

	//从redis里面查询数据
	redisKey := genFansUserCountKey(id)
	countCache, err := redis2.Client.Get(c, redisKey).Result()

	var count int64
	if err != nil {
		if errors.Is(err, redis3.Nil) {
			//build
			if err := database.Client.Model(models.FollowRelation{}).Where("follow_id = ?", id).Count(&count).Error; err != nil {
				logger.Errorf("count follow from db failed %v", err)
				response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
				return
			}
			if err := redis2.Client.Set(c, redisKey, count, redis.FansOrFollowCountExpiredTime).Err(); err != nil {
				logger.Errorf("set count to redis failed %v", err)
				response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisSetValueErrorCode, strings.InternalServiceError)
				return
			}
		} else {
			logger.Errorf("redis operation error %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
			return
		}
	} else {
		count, err = strconv.ParseInt(countCache, 10, 64)
		if err != nil {
			logger.Errorf("parse str to int64 error %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.VariableTypeConversionErrorCode, strings.InternalServiceError)
			return
		}
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, api.GetFollowOrFansCountByIdResp{
		Count: &count,
	})

	logger.Errorf("getFansCountById success")
}
